/**
 * Copyright (C) 2025 pony working team
 * SPDX-License-Identifier: MulanPSL-2.0
 */

/* includes (local) ----------------------------------------------------------*/
#include <iaper.h>
#include <ymodem.h>
/* includes (standard library, system) ---------------------------------------*/
/* includes (other library) --------------------------------------------------*/
/* includes (project) --------------------------------------------------------*/
#include <hal_uart.h>

#include <k_task.h>

#include <x_config.h>
#include <x_module.h>
#include <x_warn.h>

/* defines -------------------------------------------------------------------*/
/* typedefs ------------------------------------------------------------------*/
/* macro ---------------------------------------------------------------------*/
/* variables (extern) --------------------------------------------------------*/
/* variables (local) ---------------------------------------------------------*/
/* variables (global) --------------------------------------------------------*/
/* inlines -------------------------------------------------------------------*/
/* functions -----------------------------------------------------------------*/

static int ymodem_putchar(int c);
static int ymodem_getchar(size_t timeout);

static int uart_downloader(uintptr_t arg)
{
    ymodem_t modem = {
        .part_id = IAP_LPAR_ID_APP,
        .putchar = ymodem_putchar,
        .getchar = ymodem_getchar,
    };
    return ymodem_receive(&modem);
}

static size_t uart_filesize(uintptr_t arg)
{
    return ymodem_get_filesize();
}

static const char *uart_filename(uintptr_t arg)
{
    return ymodem_get_filename();
}

static unsigned char uart_getchar(uintptr_t arg, size_t timeout)
{
    hal_uart_t *uart = (hal_uart_t *)arg;

    return (unsigned char)hal_uart_timed_getc(uart, timeout);
}

static int uart_putchar(uintptr_t arg, unsigned char ch)
{
    hal_uart_t *uart = (hal_uart_t *)arg;

    hal_uart_putc(uart, ch);

    return 1;
}

static iaper_t uart_iaper = {
    .downloader = uart_downloader,
    .filesize = uart_filesize,
    .filename = uart_filename,
    .getc = uart_getchar,
    .putc = uart_putchar,
    .arg = 0,
};

static int ymodem_putchar(int c)
{
    return uart_iaper.putc(uart_iaper.arg, (unsigned char)c);
}

static int ymodem_getchar(size_t timeout)
{
    return uart_iaper.getc(uart_iaper.arg, timeout);
}

static int uart_iaper_init(void)
{
    hal_uart_t *uart = hal_uart_open(CONFIG_IAPER_UART_NAME);

    if (uart == NULL) {
        return -1;
    }

    uart_iaper.arg = (uintptr_t)uart;

    task_id t = task_spawn("iaper", 1, TASK_OPTION_SYSTEM, CONFIG_IAPER_UART_STACK_SIZE,
                           (int (*)(uintptr_t))iaper_loop, (uintptr_t)&uart_iaper);

    WARN_ON(t == NULL, return -1, "Fail to create iap task!");

    return 0;
}

MODULE_INIT(postdriver, uart_iaper_init);
